home *** CD-ROM | disk | FTP | other *** search
/ Whiteline: Alpha / Whiteline Alpha.iso / linux / atari / source / source.lzh / atari-linux-0.01pl3 / fs / select.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-06-05  |  6.7 KB  |  258 lines

  1. /*
  2.  * This file contains the procedures for the handling of select
  3.  *
  4.  * Created for Linux based loosely upon Mathius Lattner's minix
  5.  * patches by Peter MacDonald. Heavily edited by Linus.
  6.  *
  7.  * This file is subject to the terms and conditions of the GNU General Public
  8.  * License.  See the file README.legal in the main directory of this archive
  9.  * for more details.
  10.  */
  11.  
  12. #include <linux/types.h>
  13. #include <linux/time.h>
  14. #include <linux/fs.h>
  15. #include <linux/kernel.h>
  16. #include <linux/sched.h>
  17. #include <linux/string.h>
  18. #include <linux/stat.h>
  19. #include <linux/signal.h>
  20. #include <linux/errno.h>
  21.  
  22. #include <asm/segment.h>
  23. #include <asm/system.h>
  24.  
  25. #define ROUND_UP(x,y) (((x)+(y)-1)/(y))
  26.  
  27. /*
  28.  * Ok, Peter made a complicated, but straightforward multiple_wait() function.
  29.  * I have rewritten this, taking some shortcuts: This code may not be easy to
  30.  * follow, but it should be free of race-conditions, and it's practical. If you
  31.  * understand what I'm doing here, then you understand how the linux
  32.  * sleep/wakeup mechanism works.
  33.  *
  34.  * Two very simple procedures, select_wait() and free_wait() make all the work.
  35.  * select_wait() is a inline-function defined in <linux/sched.h>, as all select
  36.  * functions have to call it to add an entry to the select table.
  37.  */
  38.  
  39. /*
  40.  * I rewrote this again to make the select_table size variable, take some
  41.  * more shortcuts, improve responsiveness, and remove another race that
  42.  * Linus noticed.  -- jrs
  43.  */
  44.  
  45. static void free_wait(select_table * p)
  46. {
  47.     struct select_table_entry * entry = p->entry + p->nr;
  48.  
  49.     while (p->nr > 0) {
  50.         p->nr--;
  51.         entry--;
  52.         remove_wait_queue(entry->wait_address,&entry->wait);
  53.     }
  54. }
  55.  
  56. /*
  57.  * The check function checks the ready status of a file using the vfs layer.
  58.  *
  59.  * If the file was not ready we were added to its wait queue.  But in
  60.  * case it became ready just after the check and just before it called
  61.  * select_wait, we call it again, knowing we are already on its
  62.  * wait queue this time.  The second call is not necessary if the
  63.  * select_table is NULL indicating an earlier file check was ready
  64.  * and we aren't going to sleep on the select_table.  -- jrs
  65.  */
  66.  
  67. static int check(int flag, select_table * wait, struct file * file)
  68. {
  69.     struct inode * inode;
  70.     struct file_operations *fops;
  71.     int (*select) (struct inode *, struct file *, int, select_table *);
  72.  
  73.     inode = file->f_inode;
  74.     if ((fops = file->f_op) && (select = fops->select))
  75.         return select(inode, file, flag, wait)
  76.             || (wait && select(inode, file, flag, NULL));
  77.     if (S_ISREG(inode->i_mode))
  78.         return 1;
  79.     return 0;
  80. }
  81.  
  82. int do_select(int n, fd_set *in, fd_set *out, fd_set *ex,
  83.     fd_set *res_in, fd_set *res_out, fd_set *res_ex)
  84. {
  85.     int count;
  86.     select_table wait_table, *wait;
  87.     struct select_table_entry *entry;
  88.     unsigned long set;
  89.     int i,j;
  90.     int max = -1;
  91.  
  92.     for (j = 0 ; j < __FDSET_LONGS ; j++) {
  93.         i = j << 5;
  94.         if (i >= n)
  95.             break;
  96.         set = in->fds_bits[j] | out->fds_bits[j] | ex->fds_bits[j];
  97.         for ( ; set ; i++,set >>= 1) {
  98.             if (i >= n)
  99.                 goto end_check;
  100.             if (!(set & 1))
  101.                 continue;
  102.             if (!current->filp[i])
  103.                 return -EBADF;
  104.             if (!current->filp[i]->f_inode)
  105.                 return -EBADF;
  106.             max = i;
  107.         }
  108.     }
  109. end_check:
  110.     n = max + 1;
  111.     if(!(entry = (struct select_table_entry*) __get_free_page(GFP_KERNEL)))
  112.         return -ENOMEM;
  113.     FD_ZERO(res_in);
  114.     FD_ZERO(res_out);
  115.     FD_ZERO(res_ex);
  116.     count = 0;
  117.     wait_table.nr = 0;
  118.     wait_table.entry = entry;
  119.     wait = &wait_table;
  120. repeat:
  121.     current->state = TASK_INTERRUPTIBLE;
  122.     for (i = 0 ; i < n ; i++) {
  123.         if (FD_ISSET(i,in) && check(SEL_IN,wait,current->filp[i])) {
  124.             FD_SET(i, res_in);
  125.             count++;
  126.             wait = NULL;
  127.         }
  128.         if (FD_ISSET(i,out) && check(SEL_OUT,wait,current->filp[i])) {
  129.             FD_SET(i, res_out);
  130.             count++;
  131.             wait = NULL;
  132.         }
  133.         if (FD_ISSET(i,ex) && check(SEL_EX,wait,current->filp[i])) {
  134.             FD_SET(i, res_ex);
  135.             count++;
  136.             wait = NULL;
  137.         }
  138.     }
  139.     wait = NULL;
  140.     if (!count && current->timeout && !(current->signal & ~current->blocked)) {
  141.         schedule();
  142.         goto repeat;
  143.     }
  144.     free_wait(&wait_table);
  145.     free_page((unsigned long) entry);
  146.     current->state = TASK_RUNNING;
  147.     return count;
  148. }
  149.  
  150. /*
  151.  * We do a VERIFY_WRITE here even though we are only reading this time:
  152.  * we'll write to it eventually..
  153.  */
  154. static int __get_fd_set(int nr, unsigned long * fs_pointer, unsigned long * fdset)
  155. {
  156.     int error;
  157.  
  158.     FD_ZERO(fdset);
  159.     if (!fs_pointer)
  160.         return 0;
  161.     error = verify_area(VERIFY_WRITE,fs_pointer,sizeof(fd_set));
  162.     if (error)
  163.         return error;
  164.     while (nr > 0) {
  165.         *fdset = get_fs_long(fs_pointer);
  166.         fdset++;
  167.         fs_pointer++;
  168.         nr -= 32;
  169.     }
  170.     return 0;
  171. }
  172.  
  173. static void __set_fd_set(int nr, unsigned long * fs_pointer, unsigned long * fdset)
  174. {
  175.     if (!fs_pointer)
  176.         return;
  177.     while (nr > 0) {
  178.         put_fs_long(*fdset, fs_pointer);
  179.         fdset++;
  180.         fs_pointer++;
  181.         nr -= 32;
  182.     }
  183. }
  184.  
  185. #define get_fd_set(nr,fsp,fdp) \
  186. __get_fd_set(nr, (unsigned long *) (fsp), (unsigned long *) (fdp))
  187.  
  188. #define set_fd_set(nr,fsp,fdp) \
  189. __set_fd_set(nr, (unsigned long *) (fsp), (unsigned long *) (fdp))
  190.  
  191. /*
  192.  * We can actually return ERESTARTSYS insetad of EINTR, but I'd
  193.  * like to be certain this leads to no problems. So I return
  194.  * EINTR just for safety.
  195.  *
  196.  * Update: ERESTARTSYS breaks at least the xview clock binary, so
  197.  * I'm trying ERESTARTNOHAND which restart only when you want to.
  198.  */
  199. asmlinkage int sys_select( unsigned long *buffer )
  200. {
  201. /* Perform the select(nd, in, out, ex, tv) system call. */
  202.     int i;
  203.     fd_set res_in, in, *inp;
  204.     fd_set res_out, out, *outp;
  205.     fd_set res_ex, ex, *exp;
  206.     int n;
  207.     struct timeval *tvp;
  208.     unsigned long timeout;
  209.  
  210.     i = verify_area(VERIFY_READ, buffer, 20);
  211.     if (i)
  212.         return i;
  213.     n = get_fs_long(buffer++);
  214.     if (n < 0)
  215.         return -EINVAL;
  216.     if (n > NR_OPEN)
  217.         n = NR_OPEN;
  218.     inp = (fd_set *) get_fs_long(buffer++);
  219.     outp = (fd_set *) get_fs_long(buffer++);
  220.     exp = (fd_set *) get_fs_long(buffer++);
  221.     tvp = (struct timeval *) get_fs_long(buffer);
  222.     if ((i = get_fd_set(n, inp, &in)) ||
  223.         (i = get_fd_set(n, outp, &out)) ||
  224.         (i = get_fd_set(n, exp, &ex))) return i;
  225.     timeout = ~0UL;
  226.     if (tvp) {
  227.         i = verify_area(VERIFY_WRITE, tvp, sizeof(*tvp));
  228.         if (i)
  229.             return i;
  230.         timeout = jiffies;
  231.         timeout += ROUND_UP(get_fs_long((unsigned long *)&tvp->tv_usec),(1000000/HZ));
  232.         timeout += get_fs_long((unsigned long *)&tvp->tv_sec) * HZ;
  233.         if (timeout <= jiffies)
  234.             timeout = 0;
  235.     }
  236.     current->timeout = timeout;
  237.     i = do_select(n, &in, &out, &ex, &res_in, &res_out, &res_ex);
  238.     if (current->timeout > jiffies)
  239.         timeout = current->timeout - jiffies;
  240.     else
  241.         timeout = 0;
  242.     current->timeout = 0;
  243.     if (tvp) {
  244.         put_fs_long(timeout/HZ, (unsigned long *) &tvp->tv_sec);
  245.         timeout %= HZ;
  246.         timeout *= (1000000/HZ);
  247.         put_fs_long(timeout, (unsigned long *) &tvp->tv_usec);
  248.     }
  249.     if (i < 0)
  250.         return i;
  251.     if (!i && (current->signal & ~current->blocked))
  252.         return -ERESTARTNOHAND;
  253.     set_fd_set(n, inp, &res_in);
  254.     set_fd_set(n, outp, &res_out);
  255.     set_fd_set(n, exp, &res_ex);
  256.     return i;
  257. }
  258.